// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;

namespace TLens.Analyzers
{
    sealed class UnusedParametersAnalyzer : Analyzer
    {
        readonly List<(MethodDefinition, int)> methods = new List<(MethodDefinition, int)>();

        protected override void ProcessMethod(MethodDefinition method)
        {
            if (!method.HasParameters)
                return;

            if (method.IsVirtual || method.IsInternalCall)
                return;

            if (method.HasCustomAttributes && method.CustomAttributes.Any(l => l.AttributeType.Name == "IntrinsicAttribute"))
                return;

            var parameters = new BitArray(method.Parameters.Count);
            foreach (var instr in method.Body.Instructions)
            {
                int index;
                switch (instr.OpCode.Code)
                {
                    default:
                        continue;
                    case Code.Ldarg_0:
                        if (!method.IsStatic)
                            continue;

                        index = 0;
                        break;
                    case Code.Ldarg_1:
                        index = method.IsStatic ? 1 : 0;
                        break;
                    case Code.Ldarg_2:
                        index = method.IsStatic ? 2 : 1;
                        break;
                    case Code.Ldarg_3:
                        index = method.IsStatic ? 3 : 2;
                        break;
                    case Code.Ldarg_S:
                    case Code.Ldarga_S:
                        index = ((ParameterDefinition)instr.Operand).Index;
                        break;
                }

                parameters[index] = true;
            }

            for (int i = 0; i < parameters.Count; ++i)
            {
                if (parameters[i])
                    continue;

                methods.Add((method, i));
            }
        }

        public override void PrintResults(int maxCount)
        {
            var entries = methods.OrderBy(l => l.Item1.Parameters[l.Item2].ParameterType.IsPrimitive).ThenBy(l => l.Item1.FullName).Take(maxCount);
            if (!entries.Any())
                return;

            PrintHeader("Method unused parameters");

            foreach (var entry in entries)
            {
                var method = entry.Item1;
                Console.WriteLine($"Method '{method.ToDisplay()}' has unused parameter '{method.Parameters[entry.Item2].Name}'");
            }
        }
    }
}
